function Invoke-Task {
    <#
        .SYNOPSIS
        Executes another task in the current build script.

        .DESCRIPTION
        This is a function that will allow you to invoke a Task from within another Task in the current build script.

        .PARAMETER taskName
        The name of the task to execute.

        .EXAMPLE
        Invoke-Task "Compile"

        This example calls the "Compile" task.

        .LINK
        Assert
        .LINK
        Exec
        .LINK
        FormatTaskName
        .LINK
        Framework
        .LINK
        Get-PSakeScriptTasks
        .LINK
        Include
        .LINK
        Invoke-psake
        .LINK
        Properties
        .LINK
        Task
        .LINK
        TaskSetup
        .LINK
        TaskTearDown
    #>
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$taskName
    )

    Assert $taskName ($msgs.error_invalid_task_name)

    $taskKey = $taskName.ToLower()

    $currentContext = $psake.context.Peek()

    if ($currentContext.aliases.Contains($taskKey)) {
        $taskName = $currentContext.aliases.$taskKey.Name
        $taskKey = $taskName.ToLower()
    }

    Assert ($currentContext.tasks.Contains($taskKey)) ($msgs.error_task_name_does_not_exist -f $taskName)

    if ($currentContext.executedTasks.Contains($taskKey))  { return }

    Assert (!$currentContext.callStack.Contains($taskKey)) ($msgs.error_circular_reference -f $taskName)

    $currentContext.callStack.Push($taskKey)

    try {

        $task = $currentContext.tasks.$taskKey

        $precondition_is_valid = & $task.Precondition

        if (!$precondition_is_valid) {
            WriteColoredOutput ($msgs.precondition_was_false -f $taskName) -foregroundcolor Cyan
        } else {
            if ($taskKey -ne 'default') {

                if ($task.PreAction -or $task.PostAction) {
                    Assert ($null -ne $task.Action) ($msgs.error_missing_action_parameter -f $taskName)
                }

                foreach ($variable in $task.requiredVariables) {
                    Assert ((Test-Path "variable:$variable") -and ($null -ne (Get-Variable $variable).Value)) ($msgs.required_variable_not_set -f $variable, $taskName)
                }

                if ($task.Action) {

                    $stopwatch = new-object System.Diagnostics.Stopwatch

                    try {
                        foreach($childTask in $task.DependsOn) {
                            Invoke-Task $childTask
                        }
                        $stopwatch.Start()

                        $currentContext.currentTaskName = $taskName

                        try {
                            & $currentContext.taskSetupScriptBlock @($task)
                            try {
                                if ($task.PreAction) {
                                    & $task.PreAction
                                }

                                if ($currentContext.config.taskNameFormat -is [ScriptBlock]) {
                                    $taskHeader = & $currentContext.config.taskNameFormat $taskName
                                } else {
                                    $taskHeader = $currentContext.config.taskNameFormat -f $taskName
                                }
                                WriteColoredOutput $taskHeader -foregroundcolor Cyan

                                & $task.Action
                            } finally {
                                if ($task.PostAction) {
                                    & $task.PostAction
                                }
                            }
                        } catch {
                            # want to catch errors here _before_ we invoke TaskTearDown
                            # so that TaskTearDown reliably gets the Task-scoped
                            # success/fail/error context.
                            $task.Success        = $false
                            $task.ErrorMessage   = $_
                            $task.ErrorDetail    = $_ | Out-String
                            $task.ErrorFormatted = FormatErrorMessage $_



                            throw $_ # pass this up the chain; cleanup is handled higher int he stack
                        } finally {
                            & $currentContext.taskTearDownScriptBlock $task
                        }
                    } catch {
                        if ($task.ContinueOnError) {
                            "-"*70
                            WriteColoredOutput ($msgs.continue_on_error -f $taskName,$_) -foregroundcolor Yellow
                            "-"*70
                        }  else {
                            throw $_
                        }
                    } finally {
                        $task.Duration = $stopwatch.Elapsed
                    }
                } else {
                    # no action was specified but we still execute all the dependencies
                    foreach($childTask in $task.DependsOn) {
                        Invoke-Task $childTask
                    }
                }
            } else {
                foreach($childTask in $task.DependsOn) {
                    Invoke-Task $childTask
                }
            }

            Assert (& $task.Postcondition) ($msgs.postcondition_failed -f $taskName)
        }
    }
    catch {
        throw $_
    }
    finally {
        $poppedTaskKey = $currentContext.callStack.Pop()
        Assert ($poppedTaskKey -eq $taskKey) ($msgs.error_corrupt_callstack -f $taskKey,$poppedTaskKey)
    }

    $currentContext.executedTasks.Push($taskKey)
}
